From 2b41ca6037ed6642e61c5d05e94e8646d257289a Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Thu, 27 Oct 2005 17:24:06 +0100 Subject: [PATCH] This patch * adapts the Xen access control module to recent changes in the "unbound event channel" interface and its usage * adds checks that became necessary because of domain structures "hanging around" in the domain list after domain destruction This patch is tested on today's xen-unstable with Null, CHWALL, STE, CHWALL_STE policies. Signed-off by: Reiner Sailer --- xen/acm/acm_simple_type_enforcement_hooks.c | 74 ++++++++++++++------- xen/common/acm_ops.c | 13 +++- xen/include/acm/acm_hooks.h | 50 +++++++++----- 3 files changed, 94 insertions(+), 43 deletions(-) diff --git a/xen/acm/acm_simple_type_enforcement_hooks.c b/xen/acm/acm_simple_type_enforcement_hooks.c index de08f46c2a..41bb1b393c 100644 --- a/xen/acm/acm_simple_type_enforcement_hooks.c +++ b/xen/acm/acm_simple_type_enforcement_hooks.c @@ -392,8 +392,11 @@ check_cache(struct domain *dom, domid_t rdom) { int i; printkd("checking cache: %x --> %x.\n", dom->domain_id, rdom); + + if (dom->ssid == NULL) + return 0; ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, - (struct acm_ssid_domain *)(dom)->ssid); + (struct acm_ssid_domain *)(dom->ssid)); for(i=0; i< ACM_TE_CACHE_SIZE; i++) { if ((ste_ssid->ste_cache[i].valid == VALID) && @@ -412,6 +415,8 @@ cache_result(struct domain *subj, struct domain *obj) { struct ste_ssid *ste_ssid; int i; printkd("caching from doms: %x --> %x.\n", subj->domain_id, obj->domain_id); + if (subj->ssid == NULL) + return; ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, (struct acm_ssid_domain *)(subj)->ssid); for(i=0; i< ACM_TE_CACHE_SIZE; i++) @@ -431,26 +436,34 @@ clean_id_from_cache(domid_t id) struct ste_ssid *ste_ssid; int i; struct domain **pd; + struct acm_ssid_domain *ssid; printkd("deleting cache for dom %x.\n", id); - read_lock(&domlist_lock); /* look through caches of all domains */ pd = &domain_list; for ( pd = &domain_list; *pd != NULL; pd = &(*pd)->next_in_list ) { - ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, - (struct acm_ssid_domain *)(*pd)->ssid); + ssid = (struct acm_ssid_domain *)((*pd)->ssid); + + if (ssid == NULL) + continue; /* hanging domain structure, no ssid any more ... */ + ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssid); + if (!ste_ssid) { + printk("%s: deleting ID from cache ERROR (no ste_ssid)!\n", + __func__); + goto out; + } for (i=0; iste_cache[i].valid == VALID) && - (ste_ssid->ste_cache[i].id = id)) + (ste_ssid->ste_cache[i].id == id)) ste_ssid->ste_cache[i].valid = FREE; } + out: read_unlock(&domlist_lock); } /*************************** * Authorization functions **************************/ - static int ste_pre_domain_create(void *subject_ssid, ssidref_t ssidref) { @@ -484,19 +497,29 @@ ste_post_domain_destroy(void *subject_ssid, domid_t id) /* -------- EVENTCHANNEL OPERATIONS -----------*/ static int -ste_pre_eventchannel_unbound(domid_t id) { +ste_pre_eventchannel_unbound(domid_t id1, domid_t id2) { struct domain *subj, *obj; int ret; - traceprintk("%s: dom%x-->dom%x.\n", - __func__, current->domain->domain_id, id); + traceprintk("%s: dom%x-->dom%x.\n", __func__, + (id1 == DOMID_SELF) ? current->domain->domain_id : id1, + (id2 == DOMID_SELF) ? current->domain->domain_id : id2); - if (check_cache(current->domain, id)) { + if (id1 == DOMID_SELF) id1 = current->domain->domain_id; + if (id2 == DOMID_SELF) id2 = current->domain->domain_id; + + subj = find_domain_by_id(id1); + obj = find_domain_by_id(id2); + if ((subj == NULL) || (obj == NULL)) { + ret = ACM_ACCESS_DENIED; + goto out; + } + /* cache check late */ + if (check_cache(subj, obj->domain_id)) { atomic_inc(&ste_bin_pol.ec_cachehit_count); - return ACM_ACCESS_PERMITTED; + ret = ACM_ACCESS_PERMITTED; + goto out; } atomic_inc(&ste_bin_pol.ec_eval_count); - subj = current->domain; - obj = find_domain_by_id(id); if (share_common_type(subj, obj)) { cache_result(subj, obj); @@ -505,38 +528,43 @@ ste_pre_eventchannel_unbound(domid_t id) { atomic_inc(&ste_bin_pol.ec_denied_count); ret = ACM_ACCESS_DENIED; } + out: if (obj != NULL) put_domain(obj); + if (subj != NULL) + put_domain(subj); return ret; } static int -ste_pre_eventchannel_interdomain(domid_t id1, domid_t id2) +ste_pre_eventchannel_interdomain(domid_t id) { - struct domain *subj, *obj; + struct domain *subj=NULL, *obj=NULL; int ret; + traceprintk("%s: dom%x-->dom%x.\n", __func__, - (id1 == DOMID_SELF) ? current->domain->domain_id : id1, - (id2 == DOMID_SELF) ? current->domain->domain_id : id2); + current->domain->domain_id, + (id == DOMID_SELF) ? current->domain->domain_id : id); /* following is a bit longer but ensures that we * "put" only domains that we where "find"-ing */ - if (id1 == DOMID_SELF) id1 = current->domain->domain_id; - if (id2 == DOMID_SELF) id2 = current->domain->domain_id; + if (id == DOMID_SELF) id = current->domain->domain_id; - subj = find_domain_by_id(id1); - obj = find_domain_by_id(id2); - if ((subj == NULL) || (obj == NULL)) { + subj = current->domain; + obj = find_domain_by_id(id); + if (obj == NULL) { ret = ACM_ACCESS_DENIED; goto out; } + /* cache check late, but evtchn is not on performance critical path */ if (check_cache(subj, obj->domain_id)) { atomic_inc(&ste_bin_pol.ec_cachehit_count); ret = ACM_ACCESS_PERMITTED; goto out; } + atomic_inc(&ste_bin_pol.ec_eval_count); if (share_common_type(subj, obj)) { @@ -549,8 +577,6 @@ ste_pre_eventchannel_interdomain(domid_t id1, domid_t id2) out: if (obj != NULL) put_domain(obj); - if (subj != NULL) - put_domain(subj); return ret; } diff --git a/xen/common/acm_ops.c b/xen/common/acm_ops.c index 4f0d46c3e5..d290b40de6 100644 --- a/xen/common/acm_ops.c +++ b/xen/common/acm_ops.c @@ -133,7 +133,10 @@ long do_acm_op(struct acm_op * u_acm_op) struct domain *subj = find_domain_by_id(op->u.getssid.id.domainid); if (!subj) return -ESRCH; /* domain not found */ - + if (subj->ssid == NULL) { + put_domain(subj); + return -ESRCH; + } ssidref = ((struct acm_ssid_domain *)(subj->ssid))->ssidref; put_domain(subj); } else @@ -167,6 +170,10 @@ long do_acm_op(struct acm_op * u_acm_op) ret = -ESRCH; /* domain not found */ goto out; } + if (subj->ssid == NULL) { + put_domain(subj); + ret = -ESRCH; + } ssidref1 = ((struct acm_ssid_domain *)(subj->ssid))->ssidref; put_domain(subj); } else { @@ -182,6 +189,10 @@ long do_acm_op(struct acm_op * u_acm_op) ret = -ESRCH; /* domain not found */ goto out; } + if (subj->ssid == NULL) { + put_domain(subj); + return -ESRCH; + } ssidref2 = ((struct acm_ssid_domain *)(subj->ssid))->ssidref; put_domain(subj); } else { diff --git a/xen/include/acm/acm_hooks.h b/xen/include/acm/acm_hooks.h index a79acf2952..011669373d 100644 --- a/xen/include/acm/acm_hooks.h +++ b/xen/include/acm/acm_hooks.h @@ -100,10 +100,10 @@ struct acm_operations { void (*fail_domain_create) (void *subject_ssid, ssidref_t ssidref); void (*post_domain_destroy) (void *object_ssid, domid_t id); /* event channel control hooks (can be NULL) */ - int (*pre_eventchannel_unbound) (domid_t id); - void (*fail_eventchannel_unbound) (domid_t id); - int (*pre_eventchannel_interdomain) (domid_t id1, domid_t id2); - int (*fail_eventchannel_interdomain) (domid_t id1, domid_t id2); + int (*pre_eventchannel_unbound) (domid_t id1, domid_t id2); + void (*fail_eventchannel_unbound) (domid_t id1, domid_t id2); + int (*pre_eventchannel_interdomain) (domid_t id); + void (*fail_eventchannel_interdomain) (domid_t id); /* grant table control hooks (can be NULL) */ int (*pre_grant_map_ref) (domid_t id); void (*fail_grant_map_ref) (domid_t id); @@ -193,31 +193,31 @@ static inline void acm_post_domain_destroy(void *object_ssid, domid_t id) return; } -static inline int acm_pre_eventchannel_unbound(domid_t id) +static inline int acm_pre_eventchannel_unbound(domid_t id1, domid_t id2) { if ((acm_primary_ops->pre_eventchannel_unbound != NULL) && - acm_primary_ops->pre_eventchannel_unbound(id)) + acm_primary_ops->pre_eventchannel_unbound(id1, id2)) return ACM_ACCESS_DENIED; else if ((acm_secondary_ops->pre_eventchannel_unbound != NULL) && - acm_secondary_ops->pre_eventchannel_unbound(id)) { + acm_secondary_ops->pre_eventchannel_unbound(id1, id2)) { /* roll-back primary */ if (acm_primary_ops->fail_eventchannel_unbound != NULL) - acm_primary_ops->fail_eventchannel_unbound(id); + acm_primary_ops->fail_eventchannel_unbound(id1, id2); return ACM_ACCESS_DENIED; } else return ACM_ACCESS_PERMITTED; } -static inline int acm_pre_eventchannel_interdomain(domid_t id1, domid_t id2) +static inline int acm_pre_eventchannel_interdomain(domid_t id) { if ((acm_primary_ops->pre_eventchannel_interdomain != NULL) && - acm_primary_ops->pre_eventchannel_interdomain(id1, id2)) + acm_primary_ops->pre_eventchannel_interdomain(id)) return ACM_ACCESS_DENIED; else if ((acm_secondary_ops->pre_eventchannel_interdomain != NULL) && - acm_secondary_ops->pre_eventchannel_interdomain(id1, id2)) { + acm_secondary_ops->pre_eventchannel_interdomain(id)) { /* roll-back primary */ if (acm_primary_ops->fail_eventchannel_interdomain != NULL) - acm_primary_ops->fail_eventchannel_interdomain(id1, id2); + acm_primary_ops->fail_eventchannel_interdomain(id); return ACM_ACCESS_DENIED; } else return ACM_ACCESS_PERMITTED; @@ -234,10 +234,22 @@ static inline int acm_pre_dom0_op(dom0_op_t *op, void **ssid) current->domain->ssid, op->u.createdomain.ssidref); break; case DOM0_DESTROYDOMAIN: + if (*ssid != NULL) { + printkd("%s: Warning. Overlapping destruction.\n", + __func__); + return -EACCES; + } d = find_domain_by_id(op->u.destroydomain.domain); if (d != NULL) { *ssid = d->ssid; /* save for post destroy when d is gone */ - /* no policy-specific hook */ + if (*ssid == NULL) { + printk("%s: Warning. Destroying domain without ssid pointer.\n", + __func__); + put_domain(d); + return -EACCES; + } + d->ssid = NULL; /* make sure it's not used any more */ + /* no policy-specific hook */ put_domain(d); ret = 0; } @@ -248,7 +260,7 @@ static inline int acm_pre_dom0_op(dom0_op_t *op, void **ssid) return ret; } -static inline void acm_post_dom0_op(dom0_op_t *op, void *ssid) +static inline void acm_post_dom0_op(dom0_op_t *op, void **ssid) { switch(op->cmd) { case DOM0_CREATEDOMAIN: @@ -261,7 +273,8 @@ static inline void acm_post_dom0_op(dom0_op_t *op, void *ssid) case DOM0_DESTROYDOMAIN: acm_post_domain_destroy(ssid, op->u.destroydomain.domain); /* free security ssid for the destroyed domain (also if null policy */ - acm_free_domain_ssid((struct acm_ssid_domain *)ssid); + acm_free_domain_ssid((struct acm_ssid_domain *)(*ssid)); + *ssid = NULL; break; } } @@ -282,12 +295,13 @@ static inline int acm_pre_event_channel(evtchn_op_t *op) switch(op->cmd) { case EVTCHNOP_alloc_unbound: - ret = acm_pre_eventchannel_unbound(op->u.alloc_unbound.dom); + ret = acm_pre_eventchannel_unbound( + op->u.alloc_unbound.dom, + op->u.alloc_unbound.remote_dom); break; case EVTCHNOP_bind_interdomain: ret = acm_pre_eventchannel_interdomain( - current->domain->domain_id, - op->u.bind_interdomain.remote_dom); + op->u.bind_interdomain.remote_dom); break; default: ret = 0; /* ok */ -- 2.30.2